namespace cframework
{
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using Newtonsoft.Json;
    using UnityEngine.AddressableAssets;
    using UnityEngine.ResourceManagement.AsyncOperations;
    using UnityEngine.AddressableAssets.ResourceLocators;
    using UnityEngine.ResourceManagement.ResourceLocations;
    using UnityEngine.Networking;

    public static class HotUpdateAddressableMgr
    {
        public enum Status
        {
            none,
            initing,
            init_failed,
            inited,
            version_checking,
            version_check_failed,
            version_checked,
            catalog_downloading,
            catalog_download_failed,
            catalog_downloaded,
            catalog_updating,
            catalog_update_failed,
            catalog_updated,
            downloading,
            download_failed,
            downloaded,
        };

        class VersionInfo
        {
            public int version;
            public int minAppVersion;
            public long timestamp;
            public int size;
            public string md5;
        }


        public const string addressableVersionFileName = "addressable_versions.txt";
        public const string hotupdate_dir_name = "addressable";
        public const string local_bundle_config_name = "local_bundle_config.txt";

        static string latestHotUpdateAddressVersionInfo;
        static string latestCatalogFileName;

        static Status status;
        static VersionInfo latestVersionInfo;
        static string host;
        static int appVersion;

        static ArrayList keys;

        static long needDownloadSize = 0;

        static DownloadStatus downloadStatus;
        static float percentComplete;

        static HashSet<string> localBundleSet;

        public static Status GetStatus()
        {
            return status;
        }

        public static int GetLatestVersion()
        {
            return latestVersionInfo != null ? latestVersionInfo.version : 0;
        }

        static bool ParseVersionInfoByFilePath(string filePath, ref VersionInfo info)
        {
            string f = System.IO.Path.GetFileName(filePath);
            string[] infos = f.Split('_');
            if (infos.Length < 5)
            {
                return false;
            }

            info.version = int.Parse(infos[0]);
            info.minAppVersion = int.Parse(infos[1]);
            info.timestamp = long.Parse(infos[2]);
            info.size = int.Parse(infos[3]);
            info.md5 = infos[4];
            return true;
        }


        static string GetHostUrl()
        {
            return $"{host}/{hotupdate_dir_name}";
        }

        static string InternalIdTransformFunc(IResourceLocation location)
        {
            if (latestHotUpdateAddressVersionInfo == null)
            {
                return location.InternalId;
            }

            string internalId = location.InternalId;

            //Debug.LogError($"testtest2: {internalId}");

            string addressableDir = latestVersionInfo.version + "_" + latestVersionInfo.timestamp;

            if (internalId.Contains("http"))
            {
                if (internalId.EndsWith(".bundle"))
                {
                    string bundleFileName = System.IO.Path.GetFileName(internalId);

                    string newPath = null;
                    if (localBundleSet != null && localBundleSet.Contains(bundleFileName))
                    {
                        newPath = Application.streamingAssetsPath + "/" + hotupdate_dir_name + "/" + bundleFileName;
                    }
                    else
                    {
                        newPath = GetHostUrl() + "/" + addressableDir + "/" + bundleFileName;
                    }

                    //Debug.LogError($"testtest: {newPath}");
                    return newPath;
                }
                else if (internalId.Contains("catalog"))
                {
                    //if (internalId.EndsWith(".json"))
                    //{
                    //    string cacheCatalogPath = Application.persistentDataPath + "/" + hotupdate_dir_name + "/" + latestCatalogFileName + ".json";
                    //    Debug.LogError($"testtest: {cacheCatalogPath}");
                    //    return cacheCatalogPath;
                    //}
                    //else 

                }
            }

            if (internalId.Contains("catalog"))
            {
                if (internalId.EndsWith(".json"))
                {
                    string cacheCatalogPath = Application.persistentDataPath + "/" + hotupdate_dir_name + "/" + latestCatalogFileName + ".json";
                    //Debug.LogError($"testtest: {cacheCatalogPath}");
                    return cacheCatalogPath;
                }

                if (internalId.EndsWith(".hash"))
                {
                    string newPath = GetHostUrl() + "/" + addressableDir + "/" + latestCatalogFileName + ".hash";
                    //Debug.LogError($"testtest: {newPath}");
                    return newPath;
                }
            }


            return location.InternalId;
        }

        public static IEnumerator init(string host, int appVersion)
        {
            if (status != Status.none && status != Status.init_failed)
            {
                yield return false;
            }
            status = Status.initing;

            HotUpdateAddressableMgr.host = host;
            HotUpdateAddressableMgr.appVersion = appVersion;
            localBundleSet = null;

            if (string.IsNullOrEmpty(host))
            {
                status = Status.inited;
                Addressables.InternalIdTransformFunc = null;
                yield return true;
            }
            else
            {
                Addressables.InternalIdTransformFunc = InternalIdTransformFunc;

                string configPath = UnityWebRequestManager.AddFilePrefixIfNeed(Application.streamingAssetsPath + "/" + hotupdate_dir_name + "/" + local_bundle_config_name);

                var request = UnityWebRequest.Get(configPath);
                request.timeout = UnityWebRequestManager.timeout;
                yield return request.SendWebRequest();

                if (request.result == UnityWebRequest.Result.Success)
                {
                    string str = System.Text.Encoding.UTF8.GetString(request.downloadHandler.data);
                    string configStr = str;
                    if (!string.IsNullOrEmpty(configStr))
                    {
                        localBundleSet = JsonConvert.DeserializeObject(configStr, typeof(HashSet<string>)) as HashSet<string>;
                    }

                    status = Status.inited;

                    yield return true;
                }
                else
                {
                    status = Status.init_failed;
                    yield return false;
                }

            }
        }



        public static IEnumerator CheckLatestVersion()
        {
            if (status != Status.inited && status != Status.version_check_failed)
            {
                yield return UnityWebRequest.Result.DataProcessingError;
            }
            status = Status.version_checking;

            if (latestHotUpdateAddressVersionInfo != null)
            {
                status = Status.version_checked;
                yield return UnityWebRequest.Result.DataProcessingError;
            }


            if (string.IsNullOrEmpty(host))
            {
                status = Status.version_check_failed;
                yield return UnityWebRequest.Result.DataProcessingError;
            }



            string url = GetHostUrl() + $"/{addressableVersionFileName}?a=" + Random.Range(0, int.MaxValue);

            var request = UnityWebRequest.Get(url);
            request.timeout = UnityWebRequestManager.timeout;
            yield return request.SendWebRequest();


            if (request.result == UnityWebRequest.Result.Success)
            {
                string str = System.Text.Encoding.UTF8.GetString(request.downloadHandler.data);
                System.IO.StringReader ss = new System.IO.StringReader(str);

                int foundVersion = 0;
                long foundTimestamp = 0;
                string foundFileName = null;
                string foundCatalogFileName = null;



                VersionInfo tempVersionInfo = new VersionInfo();
                string line = null;
                while ((line = ss.ReadLine()) != null)
                {
                    string[] infos = line.Split(" ");

                    if (infos.Length < 3)
                    {
                        continue;
                    }



                    bool enable = bool.Parse(infos[2]);
                    if (enable || WhiteDeviceMgr.isWhite())
                    {
                        string catalogFileName = infos[1];
                        string versionInfoStr = infos[0];


                        if (ParseVersionInfoByFilePath(versionInfoStr, ref tempVersionInfo))
                        {
                            if (tempVersionInfo.minAppVersion <= appVersion)
                            {
                                if (foundFileName == null || tempVersionInfo.version > foundVersion || (tempVersionInfo.version == foundVersion && tempVersionInfo.timestamp > foundTimestamp))
                                {
                                    foundVersion = tempVersionInfo.version;
                                    foundTimestamp = tempVersionInfo.timestamp;
                                    foundFileName = versionInfoStr;
                                    foundCatalogFileName = catalogFileName;
                                    break;
                                }
                            }
                        }
                        else
                        {

                        }
                    }
                }

                if (foundFileName == null)
                {
                    status = Status.version_check_failed;
                    yield return request.result;
                }

                status = Status.version_checked;
                latestCatalogFileName = foundCatalogFileName;
                latestHotUpdateAddressVersionInfo = foundFileName;
                latestVersionInfo = new VersionInfo();
                ParseVersionInfoByFilePath(foundFileName, ref latestVersionInfo);

                yield return request.result;
            }
            else
            {
                status = Status.version_check_failed;
                yield return request.result;
            }
        }


        public static IEnumerator DownaloadCatalogWithExt(string ext)
        {
            string localPath = Application.persistentDataPath + "/" + hotupdate_dir_name + "/" + latestCatalogFileName + ext;

            if (CommonUtil.Exists(localPath))
            {
                yield return true;
            }

            string addressableDir = latestVersionInfo.version + "_" + latestVersionInfo.timestamp;
            string downloadUrl = GetHostUrl() + "/" + addressableDir + "/" + latestCatalogFileName + ext;

            var request = UnityWebRequest.Get(downloadUrl);
            request.timeout = UnityWebRequestManager.timeout;
            yield return request.SendWebRequest();

            if (request.result == UnityWebRequest.Result.Success)
            {
                System.IO.File.WriteAllBytes(localPath, request.downloadHandler.data);

                yield return true;
            }
            else
            {
                yield return true;
            }
        }


        public static IEnumerator DownloadCatalog()
        {
            if (status != Status.version_checked && status != Status.catalog_download_failed)
            {
                yield return false;
            }

            status = Status.catalog_downloading;

            if (System.IO.Directory.Exists(Application.persistentDataPath + "/" + hotupdate_dir_name))
            {
                string[] paths = System.IO.Directory.GetFiles(Application.persistentDataPath + "/" + hotupdate_dir_name, "*", System.IO.SearchOption.AllDirectories);
                for (int i = 0; i < paths.Length; i++)
                {
                    var p = paths[i];
                    string s = System.IO.Path.GetFileNameWithoutExtension(p);
                    if (s != latestCatalogFileName)
                    {
                        CommonUtil.Delete(p);
                    }
                }
            }
            else
            {
                System.IO.Directory.CreateDirectory(Application.persistentDataPath + "/" + hotupdate_dir_name);
            }

            var c = DownaloadCatalogWithExt(".json").WithResult();
            yield return c;
            var result2 = (bool)c.Current;

            status = result2 ? Status.catalog_downloaded : Status.catalog_download_failed;
            yield return result2;
        }



        public static IEnumerator Update()
        {
            if (string.IsNullOrEmpty(host))
            {
                if (status != Status.inited && status != Status.catalog_update_failed)
                {
                    yield return false;
                }
            }
            else
            {
                if (status != Status.catalog_downloaded && status != Status.catalog_update_failed)
                {
                    yield return false;
                }
            }

            status = Status.catalog_updating;



            var e = UpdateInternal().WithResult();
            yield return e;

            yield return e.Current;
        }


        static IEnumerator UpdateInternal()
        {
            var initHandle = Addressables.InitializeAsync();
            yield return initHandle;
            var handler = Addressables.CheckForCatalogUpdates(false);
            yield return handler;
            var catalogs = handler.Result;
            Addressables.Release(handler);

            if (catalogs.Count > 0)
            {
                var updateHandle = Addressables.UpdateCatalogs(catalogs, false);
                yield return updateHandle;
                Addressables.Release(updateHandle);

            }



            var locators = Addressables.ResourceLocators;

            keys = new System.Collections.ArrayList();
            // updateObjs = new List<IResourceLocator>(locators);
            foreach (var locator in locators)
            {
                foreach (var k in locator.Keys)
                {
                    keys.Add(k);
                }
            }


            AsyncOperationHandle sizeHandle = Addressables.GetDownloadSizeAsync(keys as IEnumerable);
            yield return sizeHandle;

            needDownloadSize = (long)sizeHandle.Result;
            Addressables.Release(sizeHandle);



            if (needDownloadSize <= 0)
            {
                status = Status.downloaded;
            }
            else
            {
                status = Status.catalog_updated;
            }

            yield return true;
        }



        public static IEnumerator Download()
        {
            if (status != Status.catalog_updated && status != Status.download_failed)
            {
                yield return false;
            }

            if (keys == null || keys.Count <= 0 || status == Status.downloaded)
            {
                status = Status.downloaded;
                yield return true;
            }
            else
            {
                status = Status.downloading;

                var downloadHandle = Addressables.DownloadDependenciesAsync(keys as IEnumerable, Addressables.MergeMode.Union, false);

                while (downloadHandle.Status == AsyncOperationStatus.None)
                {
                    downloadStatus = downloadHandle.GetDownloadStatus();
                    percentComplete = downloadStatus.Percent;

                    yield return null;
                }

                status = downloadHandle.Status == AsyncOperationStatus.Succeeded ? Status.downloaded : Status.download_failed;
                Addressables.Release(downloadHandle);

                yield return status == Status.downloaded;
            }
        }

        public static void GetProgress(out long totalBytes, out long downloadedBytes)
        {
            totalBytes = downloadStatus.TotalBytes;
            downloadedBytes = downloadStatus.DownloadedBytes;
        }

        public static long GetNeedDownloadSize()
        {
            return needDownloadSize;
        }

        public static float GetPercentComplete()
        {
            return percentComplete;
        }

        public static string ToInfoString()
        {
            string info = "";
            info += "status: " + status + "\n";
            info += "host: " + host + "\n";
            info += "appVersion: " + appVersion + "\n";
            info += "latestHotUpdateAddressFileName: " + latestHotUpdateAddressVersionInfo + "\n";

            return info;
        }

        public static IEnumerator ReloadCatalog()
        {
            // string cacheCatalogPath = $"{UnityEngine.Application.persistentDataPath}/com.unity.addressables/{realCatalogFileName}.json";
            string cacheCatalogPath = Application.persistentDataPath + "/" + hotupdate_dir_name + "/" + latestCatalogFileName + ".json";

            if (!System.IO.File.Exists(cacheCatalogPath))
            {
                yield return false;
            }
            else
            {
                Addressables.ClearResourceLocators();
                AsyncOperationHandle a = Addressables.LoadContentCatalogAsync(cacheCatalogPath);
                yield return a;
                var t = a.Status;
                Addressables.Release(a);
                yield return t == AsyncOperationStatus.Succeeded;
            }
        }
    }
}
